package munci.core.words;

import munci.core.Word;
import munci.core.WordImpl;
import munci.core.Dictionary.Name;
import munci.read.Token;

public class Literals {
	
	@Name("false") public static final Word.R0<Boolean> FALSE = literal(Boolean.FALSE);

	@Name("true") public static final Word.R0<Boolean> TRUE = literal(Boolean.TRUE);
	
	@Name("null") public static final Word.R0<Void> NULL = literal(null);
	
	@Name("void") public static final Word.R0<Class<Void>> VOID = literal(void.class);
			
	static <T> Word.R0<T> literal(final T value) {
		return new WordImpl.R0<T>() {
			public T call() {
				return value;
			}};
	}

	@Name(Token.Literal.ID) public static final Word.R1<Integer, Token.Literal.Int> INT_LITERAL =
		new WordImpl.R1<Integer, Token.Literal.Int>() {
			public Integer call(Token.Literal.Int tok) {
				String str = tok.image();
				if (str.startsWith("0") && str.length() > 1) {
					if ("Xx".indexOf(str.charAt(1)) > -1)
						return Integer.parseInt(str.substring(2), 16);
					else
						return Integer.parseInt(str.substring(1), 8);
				}
				return Integer.parseInt(str);
			}};
	
	@Name(Token.Literal.ID) public static final Word.R1<Long, Token.Literal.Long> LONG_LITERAL =
		new WordImpl.R1<Long, Token.Literal.Long>() {
			public Long call(munci.read.Token.Literal.Long tok) {
				String str = tok.image().substring(0, tok.image().length() - 1);
				if (str.startsWith("0") && str.length() > 1) {
					if ("Xx".indexOf(str.charAt(1)) > -1)
						return Long.parseLong(str.substring(2), 16);
					else
						return Long.parseLong(str.substring(1), 8);
				}
				return Long.parseLong(str);
			}};
			
	//TODO: Hex floats
	@Name(Token.Literal.ID) public static final Word.R1<Float, Token.Literal.Float> FLOAT_LITERAL =
		new WordImpl.R1<Float, Token.Literal.Float>() {
			public Float call(munci.read.Token.Literal.Float tok) {
				return Float.parseFloat(tok.image());
			}};
	
	@Name(Token.Literal.ID) public static final Word.R1<Double, Token.Literal.Double> DOUBLE_LITERAL =
		new WordImpl.R1<Double, Token.Literal.Double>() {
			public Double call(munci.read.Token.Literal.Double tok) {
				return Double.parseDouble(tok.image());
			}};
	
	@Name(Token.Literal.ID) public static final Word.R1<String, Token.Literal.Str> STRING_LITERAL =
		new WordImpl.R1<String, Token.Literal.Str>() {
			public String call(Token.Literal.Str tok) {
				return decodeString(tok.image().substring(1, tok.image().length() - 1));
			}};
			
	@Name(Token.Literal.ID) public static final Word.R1<Character, Token.Literal.Char> CHAR_LITERAL =
		new WordImpl.R1<Character, Token.Literal.Char>() {
			public Character call(Token.Literal.Char tok) {
				return decodeChar(tok.image().substring(1, tok.image().length() - 1));
			}};
	
	private static String decodeString(String in) {
		StringBuilder str = new StringBuilder();
		int escape = -1;
		for (int i = 0; i < in.length(); i++) {
			char c = in.charAt(i);
			if (escape < 0 && c == '\\') {
				escape = i;
			} else if (escape < 0) {
				str.append(c);
			} else {
				String seq = in.substring(escape, i + 1);
				c = decodeChar(seq);
				if (c != 0) {
					str.append(c);
					escape = -1;
				}
			}
		}
		return str.toString().intern();
	}
	
	public static String encodeString(String in) {
		StringBuilder str = new StringBuilder();
		for (char c : in.toCharArray()) {
			str.append(encodeChar(c));
		}
		return str.toString().intern();
	}
	
	// TODO: 0ctal escape codes
	private static char decodeChar(String str) {
		char c = str.charAt(0);
		if (c == '\\') {
			switch (str.charAt(1)) {
			case 'b':   return '\b';
			case 't':   return '\t';
			case 'n':   return '\n';
			case 'f':   return '\f';
			case 'r':   return '\r';
			case '\"':  return '\"';
			case '\'':  return '\'';
			case '\\':  return '\\';
			default:    return 0;	
			}
		}
		return c;
	}
	
	public static String encodeChar(char c) {
		switch (c) {
		case '\b': case '\t': case '\n': case '\f': case '\r':
		case '\"': case '\'': case '\\':
			return "\\" + c;
		default:    return Character.toString(c);	
		}
	}
}
